import fs from 'fs';
import path from 'path';
import { Context } from 'koa';

/**
 * 日志级别枚举
 */
export enum LogLevel {
  DEBUG = 'debug',
  INFO = 'info',
  WARN = 'warn',
  ERROR = 'error'
}

/**
 * 日志配置接口
 */
export interface LoggerConfig {
  level: LogLevel;
  logToFile: boolean;
  logDir: string;
  maxFileSize: number; // MB
  maxFiles: number;
  enableConsole: boolean;
  enableColors: boolean;
}

/**
 * 日志条目接口
 */
export interface LogEntry {
  timestamp: string;
  level: LogLevel;
  message: string;
  meta?: any;
  requestId?: string;
  userId?: string;
  ip?: string;
  userAgent?: string;
  url?: string;
  method?: string;
  statusCode?: number;
  responseTime?: number;
}

/**
 * 日志工具类
 */
export class Logger {
  private static instance: Logger;
  private config: LoggerConfig;
  private logStreams: Map<LogLevel, fs.WriteStream> = new Map();

  private constructor(config: Partial<LoggerConfig> = {}) {
    this.config = {
      level: LogLevel.INFO,
      logToFile: true,
      logDir: path.join(process.cwd(), 'logs'),
      maxFileSize: 10, // 10MB
      maxFiles: 5,
      enableConsole: true,
      enableColors: true,
      ...config
    };

    this.initializeLogStreams();
  }

  /**
   * 获取日志实例
   */
  static getInstance(config?: Partial<LoggerConfig>): Logger {
    if (!Logger.instance) {
      Logger.instance = new Logger(config);
    }
    return Logger.instance;
  }

  /**
   * 初始化日志流
   */
  private initializeLogStreams(): void {
    if (!this.config.logToFile) return;

    // 确保日志目录存在
    if (!fs.existsSync(this.config.logDir)) {
      fs.mkdirSync(this.config.logDir, { recursive: true });
    }

    // 为每个日志级别创建日志流
    Object.values(LogLevel).forEach(level => {
      const logFile = path.join(this.config.logDir, `${level}.log`);
      this.logStreams.set(level, fs.createWriteStream(logFile, { flags: 'a' }));
    });
  }

  /**
   * 格式化日志消息
   */
  private formatLogEntry(entry: LogEntry): string {
    const { timestamp, level, message, meta, requestId, userId, ip, userAgent, url, method, statusCode, responseTime } = entry;
    
    let logMessage = `[${timestamp}] [${level.toUpperCase()}]`;
    
    if (requestId) logMessage += ` [req:${requestId}]`;
    if (userId) logMessage += ` [user:${userId}]`;
    if (ip) logMessage += ` [ip:${ip}]`;
    if (method && url) logMessage += ` ${method} ${url}`;
    if (statusCode) logMessage += ` ${statusCode}`;
    if (responseTime) logMessage += ` ${responseTime}ms`;
    
    logMessage += ` - ${message}`;
    
    if (meta) {
      logMessage += ` | ${JSON.stringify(meta)}`;
    }
    
    return logMessage;
  }

  /**
   * 检查日志级别是否应该记录
   */
  private shouldLog(level: LogLevel): boolean {
    const levels = [LogLevel.DEBUG, LogLevel.INFO, LogLevel.WARN, LogLevel.ERROR];
    const currentLevelIndex = levels.indexOf(this.config.level);
    const logLevelIndex = levels.indexOf(level);
    return logLevelIndex >= currentLevelIndex;
  }

  /**
   * 写入日志
   */
  private writeLog(level: LogLevel, message: string, meta?: any): void {
    if (!this.shouldLog(level)) return;

    const entry: LogEntry = {
      timestamp: new Date().toISOString(),
      level,
      message,
      meta
    };

    const formattedLog = this.formatLogEntry(entry);

    // 输出到控制台
    if (this.config.enableConsole) {
      if (this.config.enableColors) {
        switch (level) {
          case LogLevel.DEBUG:
            console.debug(`\x1b[36m${formattedLog}\x1b[0m`); // 青色
            break;
          case LogLevel.INFO:
            console.info(`\x1b[32m${formattedLog}\x1b[0m`); // 绿色
            break;
          case LogLevel.WARN:
            console.warn(`\x1b[33m${formattedLog}\x1b[0m`); // 黄色
            break;
          case LogLevel.ERROR:
            console.error(`\x1b[31m${formattedLog}\x1b[0m`); // 红色
            break;
        }
      } else {
        switch (level) {
          case LogLevel.DEBUG:
            console.debug(formattedLog);
            break;
          case LogLevel.INFO:
            console.info(formattedLog);
            break;
          case LogLevel.WARN:
            console.warn(formattedLog);
            break;
          case LogLevel.ERROR:
            console.error(formattedLog);
            break;
        }
      }
    }

    // 写入文件
    if (this.config.logToFile) {
      const stream = this.logStreams.get(level);
      if (stream) {
        stream.write(`${formattedLog}\n`);
      }
    }
  }

  /**
   * 调试日志
   */
  debug(message: string, meta?: any): void {
    this.writeLog(LogLevel.DEBUG, message, meta);
  }

  /**
   * 信息日志
   */
  info(message: string, meta?: any): void {
    this.writeLog(LogLevel.INFO, message, meta);
  }

  /**
   * 警告日志
   */
  warn(message: string, meta?: any): void {
    this.writeLog(LogLevel.WARN, message, meta);
  }

  /**
   * 错误日志
   */
  error(message: string, meta?: any): void {
    this.writeLog(LogLevel.ERROR, message, meta);
  }

  /**
   * HTTP请求日志中间件 (Koa2)
   */
  static httpLogger(logger: Logger) {
    return async (ctx: Context, next: () => Promise<void>) => {
      const startTime = Date.now();
      const requestId = ctx.headers['x-request-id'] as string || Math.random().toString(36).substring(2, 15);

      // 添加请求ID到上下文
      ctx.id = requestId;

      // 记录请求开始
      logger.info('HTTP请求开始', {
        requestId,
        method: ctx.method,
        url: ctx.url,
        ip: ctx.ip,
        userAgent: ctx.headers['user-agent']
      });

      await next();

      const responseTime = Date.now() - startTime;
      const userId = ctx.user?.id;

      const logLevel = ctx.status >= 400 ? LogLevel.ERROR : LogLevel.INFO;

      logger.writeLog(logLevel, 'HTTP请求完成', {
        requestId,
        userId,
        method: ctx.method,
        url: ctx.url,
        ip: ctx.ip,
        userAgent: ctx.headers['user-agent'],
        statusCode: ctx.status,
        responseTime
      });
    };
  }

  /**
   * 关闭日志流
   */
  close(): void {
    this.logStreams.forEach(stream => {
      stream.end();
    });
    this.logStreams.clear();
  }
}

// 创建默认日志实例
export const logger = Logger.getInstance();